﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Security;

namespace RockBus.ServiceModel
{
    public class ClientBaseArgs : ICloneable
    {
        public ClientBaseArgs()
        {
        }

        public ClientBaseArgs(string endpointConfigurationName)
            : this()
        {
            this.EndpointConfigurationName = endpointConfigurationName;
        }

        public ClientBaseArgs(string endpointConfigurationName, string remoteAddressString)
            : this(endpointConfigurationName)
        {
            this.RemoteAddressString = remoteAddressString;
        }

        public ClientBaseArgs(string endpointConfigurationName, string username, string password)
            : this(endpointConfigurationName)
        {
            this.SetCredential(username, password);
        }

        public ClientBaseArgs(string endpointConfigurationName, UserNamePasswordClientCredential credential)
            : this(endpointConfigurationName)
        {
            this.SetCredential(credential);
        }

        public ClientBaseArgs(string endpointConfigurationName, string remoteAddressString, string username, string password)
            : this(endpointConfigurationName, username, password)
        {
            this.RemoteAddressString = remoteAddressString;
        }

        public ClientBaseArgs(string endpointConfigurationName, string remoteAddressString, UserNamePasswordClientCredential credential)
            : this(endpointConfigurationName, credential)
        {
            this.RemoteAddressString = remoteAddressString;
        }

        public ClientBaseArgs(Binding binding)
            : this()
        {
            this.Binding = binding;
        }

        public ClientBaseArgs(Binding binding, string remoteAddressString)
            : this(binding)
        {
            this.RemoteAddress = new EndpointAddress(remoteAddressString);
        }

        public ClientBaseArgs(Binding binding, string username, string password)
            : this(binding)
        {
            this.SetCredential(username, password);
        }

        public ClientBaseArgs(Binding binding, UserNamePasswordClientCredential credential)
            : this(binding)
        {
            this.SetCredential(credential);
        }

        public ClientBaseArgs(Binding binding, string remoteAddressString, string username, string password)
            : this(binding, username, password)
        {
            this.RemoteAddress = new EndpointAddress(remoteAddressString);
        }

        public ClientBaseArgs(Binding binding, string remoteAddressString, UserNamePasswordClientCredential credential)
            : this(binding, credential)
        {
            this.RemoteAddress = new EndpointAddress(remoteAddressString);
        }

        public InstanceContext CallbackInstance;
        public string EndpointConfigurationName;
        public string RemoteAddressString;
        public EndpointAddress RemoteAddress;
        public Binding Binding;

        internal List<BehaviorAddition> EndpointBehaviorAdditions = new List<BehaviorAddition>();

        public ClientBaseArgs AppendEndpointBehavior(IEndpointBehavior endpointBehavior)
        {
            this.EndpointBehaviorAdditions.Add(new BehaviorAddition(endpointBehavior));
            return this;
        }

        public ClientBaseArgs PrependEndpointBehavior(IEndpointBehavior endpointBehavior)
        {
            this.EndpointBehaviorAdditions.Add(new BehaviorAddition(endpointBehavior, BehaviorPosition.Begin));
            return this;
        }

        private UserNamePasswordClientCredential _credential;

        public void SetCredential(UserNamePasswordClientCredential credential)
        {
            if (null == credential)
            {
                this._credential = null;
            }
            else
            {
                this.SetCredential(
                    credential.UserName,
                    credential.Password);
            }
        }

        public void SetCredential(string username, string password)
        {
            if (string.IsNullOrEmpty(username))
            {
                this._credential = null;
            }
            else
            {
                ClientCredentials cc = new ClientCredentials();
                this._credential = cc.UserName;
                this._credential.UserName = username;
                this._credential.Password = password;
            }
        }

        public bool TryGetCredential(out UserNamePasswordClientCredential credential)
        {
            credential = null;
            if (null != this._credential)
            {
                credential = this._credential;
                return true;
            }

            return false;
        }

        public object[] GetActivatorArgs()
        {
            List<object> activatorArgs = new List<object>();
            if (null != this.CallbackInstance)
            {
                activatorArgs.Add(this.CallbackInstance);
            }
            if (null != this.Binding)
            {
                activatorArgs.Add(this.Binding);
            }
            else if (!string.IsNullOrEmpty(this.EndpointConfigurationName))
            {
                activatorArgs.Add(this.EndpointConfigurationName);
            }
            if (null != this.RemoteAddress)
            {
                activatorArgs.Add(this.RemoteAddress);
            }
            else if (!string.IsNullOrEmpty(this.RemoteAddressString))
            {
                activatorArgs.Add(this.RemoteAddressString);
            }

            if (activatorArgs.Count == 0)
            {
                return null;
            }
            return activatorArgs.ToArray();
        }

        #region ICloneable Members

        public object Clone()
        {
            ClientBaseArgs args = new ClientBaseArgs()
            {
                _credential = this._credential,
                Binding = this.Binding,
                CallbackInstance = this.CallbackInstance,
                EndpointBehaviorAdditions = new List<BehaviorAddition>(this.EndpointBehaviorAdditions),
                EndpointConfigurationName = this.EndpointConfigurationName,
                RemoteAddress = this.RemoteAddress,
                RemoteAddressString = this.RemoteAddressString,
            };
            return args;
        }

        #endregion ICloneable Members
    }

    internal enum BehaviorPosition
    {
        End = 0,
        Begin = 1,
    }

    internal class BehaviorAddition
    {
        public BehaviorAddition(IEndpointBehavior endpointBehavior)
        {
            this.EndpointBehavior = endpointBehavior;
        }

        public BehaviorAddition(IEndpointBehavior endpointBehavior, BehaviorPosition behaviorPosition)
            : this(endpointBehavior)
        {
            this.BehaviorPosition = behaviorPosition;
        }

        public BehaviorPosition BehaviorPosition { get; private set; }

        public IEndpointBehavior EndpointBehavior { get; private set; }
    }
}